﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.Office.Interop.Outlook;
using Microsoft.TeamFoundation.Client;

namespace Outlook.WorkItems.Extension.Manager.TeamProjectManager
{
    /// <summary>
    /// Manager of Team Project Links
    /// </summary>
    public static class TeamProjectLinksManager
    {
        /// <summary>
        /// File Path that contains Link Information between MAPIFolder and Team Project
        /// </summary>
        private static readonly string AppDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\OWIPlugin\\FolderLinks.owi";
        /// <summary>
        /// Dictionary of Team Project Link
        /// </summary>
        private static readonly Dictionary<string, TeamProjectLink> TeamProjectLinks;
        /// <summary>
        /// Dictionary of WorkItem Store
        /// </summary>
        private static readonly Dictionary<string, WorkItemStore> WorkItemsStores;

        /// <summary>
        /// Initialize list of Team Project Links
        /// </summary>
        static TeamProjectLinksManager()
        {
            TeamProjectLinks = new Dictionary<string, TeamProjectLink>();
            WorkItemsStores = new Dictionary<string, WorkItemStore>();

            var updateServerUri = false;
            if (File.Exists(AppDataFolder))
            {
                var xdoc = XDocument.Load("file:///" + AppDataFolder);
                foreach (var item in xdoc.Root.Elements("FolderLink"))
                {
                    var tpl = new TeamProjectLink()
                    {
                        FolderEntryId = item.Attribute("entryId").Value,
                        FolderStoreId = item.Attribute("storeId").Value,
                        TeamProjectName = item.Element("LinkTFS").Attribute("teamProject").Value,
                        TeamServerName = item.Element("LinkTFS").Attribute("teamServer").Value,
                    };
                    if (item.Element("LinkTFS").Elements().Any(xelt => xelt.Name == "CreateWIAction"))
                    {
                        tpl.CreateWiAction = new CreateWiAction();
                        tpl.CreateWiAction.DeserializeAction(item.Element("LinkTFS").Element("CreateWIAction"));
                    }
                    if (item.Element("LinkTFS").Elements().Any(xelt => xelt.Name == "UploadAttachmentsAction"))
                    {
                        tpl.UploadAttachmentsAction = new UploadAttachmentsAction();
                        tpl.UploadAttachmentsAction.DeserializeAction(item.Element("LinkTFS").Element("UploadAttachmentsAction"));
                    }
                    if (item.Element("LinkTFS").Attribute("teamServerUri") != null)
                        tpl.TeamServerUri = item.Element("LinkTFS").Attribute("teamServerUri").Value;
                    else
                        updateServerUri = true;

                    TeamProjectLinks.Add(tpl.FolderEntryId, tpl);
                }
            }

            if (updateServerUri)
            {
                UpdateLinkWithServerUri();
            }
        }

        private static void UpdateLinkWithServerUri()
        {
            File.Delete(AppDataFolder);

            var list = GetAllTeamProjectLink();

            TeamProjectLinks.Clear();

            foreach (var item in list)
            {
                try
                {
                    //var collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(item.TeamServerName));
                    var store = new WorkItemStore(item.TeamServerName);
                    item.TeamServerUri = store.TeamProjectCollection.Uri.ToString();
                    AddTeamProjectLink(item.FolderEntryId, item.FolderStoreId, item.TeamServerName, item.TeamServerUri, item.TeamProjectName, item.CreateWiAction, item.UploadAttachmentsAction);
                }
                catch { }
            }
        }

        /// <summary>
        /// Create and Add specified Team Project Link
        /// </summary>
        /// <param name="entryId">MAPIFolder EntryId</param>
        /// <param name="storeId">MAPIFolder StoreId</param>
        /// <param name="teamServer">Team Server Name</param>
        /// <param name="teamServerUri"></param>
        /// <param name="teamProject">Team Project Name</param>
        /// <param name="createWiAction">Settings of create work item action</param>
        /// <param name="uploadAction">Settings of upload attachments action</param>
        public static void AddTeamProjectLink(string entryId, string storeId, string teamServer, string teamServerUri, string teamProject, CreateWiAction createWiAction, UploadAttachmentsAction uploadAction)
        {
            var appFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\OWIPlugin";
            if (Directory.Exists(appFolder) == false)
            {
                Directory.CreateDirectory(appFolder);
            }
            TeamProjectLink tpl;
            if (ContainsTeamProjectLink(entryId))
                tpl = GetTeamProjectLink(entryId);
            else
            {
                tpl = new TeamProjectLink() { FolderEntryId = entryId };
                TeamProjectLinks.Add(tpl.FolderEntryId, tpl);
            }

            //tpl.FolderEntryID = entryId;
            tpl.FolderStoreId = storeId;
            tpl.TeamProjectName = teamProject;
            tpl.TeamServerName = teamServer;
            tpl.CreateWiAction = createWiAction;
            tpl.UploadAttachmentsAction = uploadAction;
            tpl.TeamServerUri = teamServerUri;

            XDocument xdoc = null;
            if (File.Exists(AppDataFolder))
            {
                xdoc = XDocument.Load("file:///" + AppDataFolder);

                var root = (from xelt in xdoc.Root.Elements()
                            where xelt.Attribute("entryId").Value == entryId
                            select xelt).FirstOrDefault();
                if (root != null)
                {
                    root.Remove();
                }
            }
            else
            {
                xdoc = new XDocument(new XElement("FolderLinks"));
            }

            var elt = new XElement("FolderLink",
                                                new XAttribute("entryId", entryId),
                                                new XAttribute("storeId", storeId),
                                                new XElement("LinkTFS",
                                                    new XAttribute("teamServer", teamServer),
                                                    new XAttribute("teamProject", teamProject),
                                                    new XAttribute("teamServerUri", teamServerUri)));

            if (createWiAction != null)
            {
                elt.Element("LinkTFS").Add(createWiAction.SerializeAction());
            }
            if (uploadAction != null)
            {
                elt.Element("LinkTFS").Add(uploadAction.SerializeAction());
            }
            xdoc.Root.Add(elt);
            xdoc.Save(AppDataFolder);
        }

        public static bool ContainsTeamProjectLink(string entryId)
        {
            return TeamProjectLinks.ContainsKey(entryId);
        }

        /// <summary>
        /// Return the Team Project Link with specified MAPIFolder entryId 
        /// </summary>
        /// <param name="entryId"></param>
        /// <returns></returns>
        public static TeamProjectLink GetTeamProjectLink(string entryId)
        {
            if (ContainsTeamProjectLink(entryId) == false)
                throw new ApplicationException("No Team Project Link exist for this MAPIFolder");

            return TeamProjectLinks[entryId];
        }

        /// <summary>
        /// Return list of all configured Team Project Link 
        /// </summary>
        /// <returns></returns>
        public static List<TeamProjectLink> GetAllTeamProjectLink()
        {
            return TeamProjectLinks.Values.ToList();
        }

        /// <summary>
        /// Return the WorkItemStore instance
        /// </summary>
        /// <param name="teamServerName"></param>
        /// <returns></returns>
        public static WorkItemStore GetWorkItemStore(string teamServerName)
        {
            if (WorkItemsStores.ContainsKey(teamServerName) == false)
                WorkItemsStores.Add(teamServerName, new WorkItemStore(teamServerName));
            return WorkItemsStores[teamServerName];
        }

        /// <summary>
        /// Remove the Team Project Link of a MAPIFolder EntryId
        /// </summary>
        /// <param name="entryId">MAPIFolder identity</param>
        public static void RemoveProjectLink(string entryId)
        {
            if (ContainsTeamProjectLink(entryId) == false)
                return;
            TeamProjectLinks.Remove(entryId);

            if (!File.Exists(AppDataFolder)) 
                return;

            var xdoc = XDocument.Load("file:///" + AppDataFolder);
            var item = (from elt in xdoc.Root.Elements()
                        where elt.Attribute("entryId").Value == entryId
                        select elt).FirstOrDefault();

            if (item != null)
                item.Remove();

            xdoc.Save(AppDataFolder);
        }

        public static void MoveMailAssociatedToWorkItems(MAPIFolder from, MAPIFolder to, string workItemState)
        {
            if (from != null && to != null)
            {
                var tpl = TeamProjectLinksManager.GetTeamProjectLink(from.EntryID);

                var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tpl.TeamServerUri));
                var wis = tfs.GetService<WorkItemStore>();

                foreach (var item in from.Items)
                {
                    try
                    {
                        var message = item as MailItem;
                        if (message == null) 
                            continue;

                        var relatedWorkItemsId = message.ItemProperties["RelatedWIIds"];
                        if (relatedWorkItemsId == null || relatedWorkItemsId.Value == null) 
                            continue;

                        var workItems = new List<WorkItem>();
                        string wiIds = relatedWorkItemsId.Value.ToString();
                        var splittedIds = wiIds.Split(';');

                        foreach (var wiId in splittedIds)
                        {
                            int id;
                            if (Int32.TryParse(wiId, out id))
                            {
                                workItems.Add(wis.GetWorkItem(id));
                            }
                        }
                        if (workItems.Count > 0 && workItems.All(wi => wi.State == workItemState))
                        {
                            message.Move(to);
                        }
                    }
                    catch { }
                }
            }
        }
    }
}
